package mobi.smarthosts;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

public class HomeActivity extends Activity implements DownloadHostsTask.TaskHandler {

    private static final int DIALOG_NO_UPDATE   = 0;
    private static final int DIALOG_CHANGE_SITE = 1;
    private static final int DIALOG_TOGGLE      = 2;
    private static final int DIALOG_ABOUT       = 4;
    private static final int DIALOG_SELECT_EDIT = 5;

    private SharedPreferences mPref;
    private Configuration mConf;

    //0 is Original, 1 is SmartHosts, 2 is Ads block
    private Hosts[] mHostsArray;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.home);

        mPref = PreferenceManager.getDefaultSharedPreferences(this);
        mConf = Configuration.getInstance();

        findViewById(R.id.btn_applySmartHosts).setOnClickListener(mClickListener);
        findViewById(R.id.btn_changeSite).setOnClickListener(mClickListener);
        findViewById(R.id.btn_toggle).setOnClickListener(mClickListener);
        findViewById(R.id.btn_editHosts).setOnClickListener(mClickListener);
        findViewById(R.id.chk_checkUpdate).setOnClickListener(mClickListener);

        drawViewComponents();

        initializeEnv();

        showReleaseNote();
    }

    @Override
    protected void onResume() {
        super.onResume();
        drawViewComponents();
    }

    private void initializeEnv() {
        if (!ShellExecutor.isRootedDevice()) {
            ViewHelper.showAlertDialog(this,
                    getString(R.string.title_default),
                    getString(R.string.msg_noRoot),
                    getString(R.string.label_ok), null);
            return;
        }

        if (!ShellExecutor.mountSystemFolder())
            ViewHelper.showAlertDialog(this,
                    getString(R.string.title_default),
                    getString(R.string.msg_noMount),
                    getString(R.string.label_ok), null);

        // Check if user have enough permission.
        String res = ShellExecutor.executeRootCommand("chmod 666 "+Configuration.HOSTS_SYSTEM.getAbsolutePath());
        if (!Utils.isEmptyString(res)) {
            ViewHelper.showAlertDialog(this,
                    getString(R.string.title_error),
                    getString(R.string.msg_permissionError),
                    getString(R.string.label_ok), null);
        }

        // Check if /etc/hosts is valid SmartHosts,
        // if it's not smarthosts, backup to /etc/hosts_orig.
        // if empty, generate new one.
        boolean needInit = false;
        boolean needBackup = false;
        String hostsHeader = ShellExecutor.executeRootCommand("head -1 "+Configuration.HOSTS_SYSTEM);
        if (Utils.isEmptyString(hostsHeader)) {
            needInit = true;
            SharedPreferences.Editor editor = mPref.edit();
            editor.putBoolean(PrefKeys.KEY_USING_SMART_HOSTS, false);
            editor.commit();
        } else if (!hostsHeader.startsWith(Hosts.META_KEY_SMARTHOSTS_ANDROID)
                && !hostsHeader.startsWith(Hosts.META_KEY_LAGECY_EARMARK)) {
            needBackup = true;
            SharedPreferences.Editor editor = mPref.edit();
            editor.putBoolean(PrefKeys.KEY_USING_SMART_HOSTS, false);
            editor.commit();
        }

        // Make clean hosts file
        if (!Utils.isExistedFile(Configuration.HOSTS_CLEAN)) {
            Log.i("Clean hosts file is not existed, create a new one");
            ShellExecutor.rootWriteFile(Configuration.HOSTS_CLEAN.getAbsolutePath(),
                    Hosts.META_KEY_SMARTHOSTS_ANDROID+"\n"+Hosts.META_KEY_CLEAN_HOSTS, false);
        }

        // Backup original hosts file. If has legacy SmartHosts, use clean hosts instead of
        // system one (which is legacy).
        if (needInit && !Utils.isExistedFile(Configuration.HOSTS_ORIGINAL)) {
            Log.i("Making new hosts file backup.");
            ShellExecutor.rootWriteFile(Configuration.HOSTS_CLEAN.getAbsolutePath(),
                    Hosts.META_KEY_SMARTHOSTS_ANDROID+"\n"+getString(R.string.msg_originalHostsReadMe), false);
        }

        // Backup hosts file that is not generated by SmartHosts.
        if (needBackup) {
            Log.i("Found hosts file needed backup to %s.", Configuration.HOSTS_ORIGINAL);
            ViewHelper.showAlertDialog(this,
                    getString(R.string.title_alert),
                    getString(R.string.msg_backupToOriginal),
                    getString(R.string.label_ok), null);
            ShellExecutor.rootCopyFile(Configuration.HOSTS_SYSTEM, Configuration.HOSTS_ORIGINAL);
            ShellExecutor.executeRootCommand(
                    "echo \""+getString(R.string.msg_originalHostsReadMe)+"\" | cat - "+Configuration.HOSTS_SYSTEM+" > "+Configuration.HOSTS_ORIGINAL,
                    "chmod 666 "+Configuration.HOSTS_ORIGINAL+"\n");
        }
    }

    private void showReleaseNote() {
        if (!mConf.getVersionName().equals(mPref.getString(PrefKeys.KEY_RELEASE_NOTE_SHOWED, null))) {
            ViewHelper.showAlertDialog(this,
                    getString(R.string.title_releaseNote),
                    getString(R.string.release_note),
                    getString(R.string.label_ok), null);
            SharedPreferences.Editor editor = mPref.edit();
            editor.putString(PrefKeys.KEY_RELEASE_NOTE_SHOWED, mConf.getVersionName());
            editor.commit();
        }
    }

    private void drawViewComponents() {
        if (mPref.getBoolean(PrefKeys.KEY_USING_SMART_HOSTS, false))
            ViewHelper.setViewText(R.id.btn_applySmartHosts, this, R.string.label_checkUpdate);
        else ViewHelper.setViewText(R.id.btn_applySmartHosts, this, R.string.label_applySmartHosts);

        long lastUpdateDateLong = mPref.getLong(PrefKeys.KEY_LAST_UPDATE_DATE, 0);
        Date lastUpdateDate = lastUpdateDateLong != 0 ? new Date(lastUpdateDateLong) : null;
        ViewHelper.setViewText(R.id.text_lastUpdate, this, lastUpdateDate != null
                ? Utils.formatDateTime(lastUpdateDate) : getString(R.string.msg_neverUpdate));
        ViewHelper.setViewText(R.id.text_updateSite, this, getLabelByValueFromArray(
                getResources().getStringArray(R.array.label_updateSites),
                getResources().getStringArray(R.array.value_updateSites),
                mConf.getUpdateSiteUrl(this)));
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        switch (id) {
        case DIALOG_NO_UPDATE:
            builder.setIcon(android.R.drawable.ic_dialog_info)
                   .setMessage(R.string.msg_noUpdate)
                   .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int id) {
                           startSectionActivity();
                       }
                   })
                   .setNegativeButton(R.string.label_cancel, new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int id) {
                           dialog.dismiss();
                       }
                   });
            return builder.create();

        case DIALOG_CHANGE_SITE:
            final String[] updateSiteLables = getResources().getStringArray(R.array.label_updateSites);
            final String[] updateSites = getResources().getStringArray(R.array.value_updateSites);
            int currentIndex = Arrays.binarySearch(updateSites, mConf.getUpdateSiteUrl(this));

            builder.setIcon(android.R.drawable.ic_dialog_info)
                   .setSingleChoiceItems(updateSiteLables, currentIndex,
                                         new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int optIdx) {
                    SharedPreferences.Editor editor = mPref.edit();
                    editor.putString(PrefKeys.KEY_UPDATE_SITE, updateSites[optIdx]);
                    editor.commit();
                    dialog.dismiss();
                    drawViewComponents();
                }
            });
            return builder.create();

        case DIALOG_TOGGLE:
            final ArrayList<String> options = new ArrayList<String>();
            options.add(getString(R.string.label_toggleSmartHosts));
            options.add(getString(R.string.label_toggleOriginal));
            options.add(getString(R.string.label_toggleClean));

            builder.setIcon(android.R.drawable.ic_dialog_info)
                   .setSingleChoiceItems(options.toArray(new String[0]), -1,
                                         new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int optIdx) {
                    boolean usingStatus;
                    if (options.get(optIdx).equals(getString(R.string.label_toggleSmartHosts))) {
                        usingStatus = true;
                        ShellExecutor.rootCopyFile(Configuration.HOSTS_BAK, Configuration.HOSTS_SYSTEM);

                    } else if (options.get(optIdx).equals(getString(R.string.label_toggleOriginal))) {
                        usingStatus = false;
                        ShellExecutor.rootCopyFile(Configuration.HOSTS_ORIGINAL, Configuration.HOSTS_SYSTEM);

                    } else if (options.get(optIdx).equals(getString(R.string.label_toggleClean))) {
                        usingStatus = false;
                        ShellExecutor.rootCopyFile(Configuration.HOSTS_CLEAN, Configuration.HOSTS_SYSTEM);

                    } else throw new SmartHostsException("Unknow toggle option");

                    SharedPreferences.Editor editor = mPref.edit();
                    editor.putBoolean(PrefKeys.KEY_USING_SMART_HOSTS, usingStatus);
                    editor.commit();
                    dialog.dismiss();
                    Toast.makeText(HomeActivity.this, R.string.msg_reboot, Toast.LENGTH_SHORT).show();
                    drawViewComponents();
                }
            });
            return builder.create();

        case DIALOG_SELECT_EDIT:
            final ArrayList<String> editOptions = new ArrayList<String>();
            editOptions.add(getString(R.string.label_editSystemHosts));
            editOptions.add(getString(R.string.label_editOriginalHosts));

            builder.setIcon(android.R.drawable.ic_dialog_info)
                   .setSingleChoiceItems(editOptions.toArray(new String[0]), -1,
                                         new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int optIdx) {

                    String hsotsPath = null;
                    if (editOptions.get(optIdx).equals(getString(R.string.label_editSystemHosts))) {
                        hsotsPath = Configuration.HOSTS_SYSTEM.getAbsolutePath();

                    } else if (editOptions.get(optIdx).equals(getString(R.string.label_editOriginalHosts))) {
                        hsotsPath = Configuration.HOSTS_ORIGINAL.getAbsolutePath();

                    } else throw new SmartHostsException("Unknow edit option");

                    Intent intent = new Intent(HomeActivity.this, HostsEditorActivity.class);
                    intent.putExtra(HostsEditorActivity.EXTRA_FILE_PATH, hsotsPath);
                    startActivity(intent);
                    dialog.dismiss();
                }
            });
            return builder.create();

        case DIALOG_ABOUT:
            View view = LayoutInflater.from(this).inflate(R.layout.about_dialog, null);
            ViewHelper.setViewText(R.id.text, view, getString(R.string.msg_about, mConf.getVersionName(), mConf.getBuildDate()));
            ((TextView) view.findViewById(R.id.text)).setGravity(android.view.Gravity.LEFT);
            builder.setTitle(R.string.label_about)
                   .setView(view)
                   .setIcon(android.R.drawable.ic_dialog_info)
                   .setCancelable(true)
                   .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int id) {
                           dialog.dismiss();
                      }
                  });
            return builder.create();

        default:
            return super.onCreateDialog(id);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.home_options_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_about:
            showDialog(DIALOG_ABOUT);
            break;

        case R.id.menu_help:
            startActivity(IntentHelper.getBrowserIntent(getString(R.string.msg_help_url)));
            break;

        case R.id.menu_feedback:
            // Log some useful information so they will be captured by feedback attachment
            Log.i("hosts line count: %s", ShellExecutor.executeRootCommand("wc -l /etc/hosts"));
            // Send email intent
            Intent intent = IntentHelper.getEmailIntent(
                    mConf.getAppName(this) + " v" + mConf.getVersionName(),
                    getString(R.string.email_reportProblem_body)
                            + DeviceHelper.generateDebugString(HomeActivity.this),
                    new String[]{Configuration.CONTACT_SUPPORT},
                    DeviceHelper.getDumpFiles(HomeActivity.this));
            startActivity(intent);
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    private OnClickListener mClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (v.getId() == R.id.btn_applySmartHosts) {
                DownloadHostsTask task = new DownloadHostsTask(HomeActivity.this, true);
                task.execute();

            } else if (v.getId() == R.id.btn_changeSite) {
                showDialog(DIALOG_CHANGE_SITE);

            } else if (v.getId() == R.id.btn_toggle) {
                showDialog(DIALOG_TOGGLE);

            } else if (v.getId() == R.id.btn_editHosts) {
                showDialog(DIALOG_SELECT_EDIT);

            } else if (v.getId() == R.id.chk_checkUpdate) {
                CheckBox checkbox = (CheckBox) v;
                SharedPreferences.Editor editor = mPref.edit();
                editor.putBoolean(PrefKeys.KEY_CHECK_UPDATE, checkbox.isChecked());
                editor.commit();
            }
        }
    };

    @Override
    public void onDownloadCompleted(Hosts[] hostsArray) {
        if (Utils.isEmptyArray(hostsArray)) return;
        // Error should has been reported by DownloadHostsTask

        // Avoid NPE if parse update Date failed.
        if (hostsArray[0].updateDate == null)
            hostsArray[0].updateDate = new Date();

        mHostsArray = hostsArray;
        // Check new download hosts with last download version
        String lastUpdateMd5 = mPref.getString(PrefKeys.KEY_LAST_UPDATE_MD5, null);
        long lastUpdateDateLong = mPref.getLong(PrefKeys.KEY_LAST_UPDATE_DATE, 0);
        Date lastUpdateDate = lastUpdateDateLong != 0 ? new Date(lastUpdateDateLong) : null;
        boolean noUpdate = ((lastUpdateMd5 != null && hostsArray[0].md5.equals(lastUpdateMd5))
                         || (lastUpdateDate != null && hostsArray[0].updateDate.equals(lastUpdateDate)));

        // If no update, alert user
        if (noUpdate) showDialog(DIALOG_NO_UPDATE);
        else startSectionActivity();
    }

    private void startSectionActivity() {
        if (Utils.isEmptyArray(mHostsArray)) {
            Log.w("mSection is empty");
            return;
        }

        LinkedHashMap<String, String> sections = new LinkedHashMap<String, String>();
        for (Hosts hosts : mHostsArray) {
            if (hosts == null || Utils.isEmptyMap(hosts.sections)) continue;
            sections.putAll(hosts.sections);
        }
        sections.put(getString(R.string.label_original), Configuration.HOSTS_ORIGINAL.getAbsolutePath());

        Intent intent = new Intent(this, SectionsActivity.class);
        intent.putExtra(SectionsActivity.EXTRA_SECTIONS, sections);
        // EXTRA_SECTION_NAMES is needed because serializing a map will break the sequence.
        intent.putExtra(SectionsActivity.EXTRA_SECTION_NAMES, sections.keySet().toArray(new String[0]));
        intent.putExtra(SectionsActivity.EXTRA_LAST_UPDATE_DATE, mHostsArray[0].updateDate);
        intent.putExtra(SectionsActivity.EXTRA_LAST_UPDATE_MD5, mHostsArray[0].md5);
        startActivity(intent);
    }

    public static String getLabelByValueFromArray(String[] labelsArray,
            String[] valuesArray, String value) {
        if (labelsArray == null || valuesArray == null || value == null) return null;
        for (int i=0; i<valuesArray.length; i++) {
            if (value.equals(valuesArray[i])) return labelsArray[i];
        }
        return "";
    }

}